#include "ParallelFwd3.h"

bool ParallelFwd3::prefix(char *s1, char *s2) {
  for ( ; *s1==*s2; ++s1, ++s2) {
    if (*(s2+1)=='\0') return true;
  }
  return false;
}

bool ParallelFwd3::substr(char *s1, char *s2) {
  int size1 = 0;
  int size2 = 0;
  while (s1[size1]!='\0') size1++;
  while (s2[size2]!='\0') size2++;
  if (size1==size2) return !strcmp(s1, s2);
  if (size1<size2) return false;
  for (int i=0; i<size1-size2+1; i++) {
    bool failed = false;
    for (int j=0; j<size2; j++) {
      if (s1[i+j-1]!=s2[j]) {
	failed = true;
	break;
      }
    }
    if (! failed) return true;
  }
  return false;
}

void ParallelFwd3::evalConstraint(int constraintIndex, int attrId, BFilter inputBF, std::vector<float>& windowsList, int& ns, int& lastNs) {
  BFilter constrNames=input.constrNames[attrId][constraintIndex];
  if((inputBF & constrNames)!=constrNames) return;
  int filterIdx;
  if (input.values[attrId].type==INT) {
    IntOMPConstraint3 *constr = (IntOMPConstraint3 *) input.constraints[attrId];
    filterIdx = constr[constraintIndex].filter;
    if ((constr[constraintIndex].op==EQ && input.values[attrId].intVal!=constr[constraintIndex].value) ||
	(constr[constraintIndex].op==LT && input.values[attrId].intVal>=constr[constraintIndex].value) ||
	(constr[constraintIndex].op==GT && input.values[attrId].intVal<=constr[constraintIndex].value) ||
	(constr[constraintIndex].op==DF && input.values[attrId].intVal==constr[constraintIndex].value))
      return;
  } else {
    StringOMPConstraint3 *constr = (StringOMPConstraint3 *) input.constraints[attrId];
    filterIdx = constr[constraintIndex].filter;
    if ((constr[constraintIndex].op==EQ &&  strcmp(input.values[attrId].stringVal, constr[constraintIndex].value)) ||
	(constr[constraintIndex].op==DF && !strcmp(input.values[attrId].stringVal, constr[constraintIndex].value)) ||
	(constr[constraintIndex].op==PF && !prefix(input.values[attrId].stringVal, constr[constraintIndex].value)) ||
	(constr[constraintIndex].op==IN && !substr(input.values[attrId].stringVal, constr[constraintIndex].value)))
      return;
  }
  filtersCount[filterIdx]++;
  if (filtersCount[filterIdx]>=filtersInfos[filterIdx].numConstraints) {
	  //********************************** PROBABILMENTE è QUI CHE DEVO AGIRE ********************************************//
	  ns++;
	  windowsList.push_back(filtersInfos[filterIdx].window);
	  if (filtersInfos[filterIdx].occurrence == 1){
		  lastNs++;
	  }
	  //**************************************** FINE MIO INTERVENTO *****************************************************//
	  interfaces[filtersInfos[filterIdx].interface] = true;
	  //cout << filtersInfos[filterIdx].interface << endl;
	  //interfaces[filterIdx] = true; //------------------------------>>>> MODIFICATA
  }
}

static inline void add(BFilter &bf1, const char* name) {
  //bf1 = bf1 | 1u << ((name[0]+name[1]+name[2]+name[3])%(sizeof(BFilter)*8));
  //bf1 = bf1 | 1u << ((name[4]+name[5]+name[6]+name[7])%(sizeof(BFilter)*8));
  bf1 = bf1 | 1u << ((name[0]+name[1])%(sizeof(BFilter)*8));
  bf1 = bf1 | 1u << ((name[2]+name[3])%(sizeof(BFilter)*8));
  bf1 = bf1 | 1u << ((name[4]+name[5])%(sizeof(BFilter)*8));
  bf1 = bf1 | 1u << ((name[6]+name[7])%(sizeof(BFilter)*8));
}

ParallelFwd3::ParallelFwd3() {
  numInterfaces = 0;
  numFilters = 0;
  consolidated = false;
  inputTime = 0;
  execTime = 0;
  resultsTime = 0;
  //omp_set_num_threads(6);
}

ParallelFwd3::~ParallelFwd3() {
  for (map<int, set<OMPFilter *> >::iterator it=hostFilters.begin(); it!=hostFilters.end(); ++it) {
    for (set<OMPFilter *>::iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) {
      OMPFilter *cf = *it2;
      delete cf;
    }
  }
  if (! consolidated) return;
  for (map<string_t, void *>::iterator it=nameConstraints.begin(); it!=nameConstraints.end(); ++it) {
    if(nameType[it->first]==INT) {
      IntOMPConstraint3 *constr = (IntOMPConstraint3 *) it->second;
      free(constr);
    } else {
      StringOMPConstraint3 *constr = (StringOMPConstraint3 *) it->second;
      free(constr);
    }
  }
  free(filtersInfos);
  free(filtersCount);
  free(interfaces);
}

void ParallelFwd3::ifConfig(int interfaceId, set<OMPFilter *> &filters) {
	// record the set of filters associated to this interface
  hostFilters.insert(make_pair(interfaceId, filters));
  //cout << "ciao!" << endl;
  // update the constraintsSize and nameType data structures (to be used at consolidate time)
  for (set<OMPFilter *>::iterator it=filters.begin(); it!=filters.end(); ++it) {
    OMPFilter *filter = *it;
    for (int i=0; i<filter->numConstraints; i++) {
      string_t name = filter->constraints[i].name;
      map<string_t, int>::iterator it=numConstraints.find(name);
      if (it==numConstraints.end()) {
	numConstraints.insert(make_pair(name, 1));
      } else {
	it->second++;
      }
      map<string_t, OMPType>::iterator it1=nameType.find(name);
      if (it1==nameType.end()) {
        nameType.insert(make_pair(name, filter->constraints[i].value.type));
      }
    }
    numFilters++;
  }
  numInterfaces++;
}

void ParallelFwd3::consolidate() {
	// Creates and sets the interfaces data structure
  interfaces = (bool*) malloc(sizeof(bool)*numInterfaces);
  memset(interfaces, 0, sizeof(bool)*numInterfaces);
  // Creates and sets the filtersCount data structure
  filtersCount = (unsigned short *) malloc(sizeof(unsigned short)*numFilters);
#pragma omp parallel for
  for (int i=0; i<numFilters; i++) {
    filtersCount[i] = 0x00u;
  }
  map<string_t, int> currentSize;
  for (map<string_t, int>::iterator it=numConstraints.begin(); it!=numConstraints.end(); ++it) {
    string_t name = it->first;
    int size = it->second;
    void *arrayVal;
    if(nameType[name]==INT) {
      arrayVal = malloc(sizeof(IntOMPConstraint3)*size);
    } else {
      arrayVal = malloc(sizeof(StringOMPConstraint3)*size);
    }
    nameConstraints.insert(make_pair(name, arrayVal));
    BFilter * constrNames = (BFilter *) malloc(sizeof(BFilter)*size);
    nameConstrNames.insert(make_pair(name, constrNames));
    currentSize.insert(make_pair(name, 0));
  }
  // Creates and sets the filtersConstraints and filtersInterface data structures
  filtersInfos = (OMPFilterInfo*) malloc(sizeof(OMPFilterInfo)*numFilters);
  int filterId = 0;
  for (map<int, set<OMPFilter *> >::iterator it=hostFilters.begin(); it!=hostFilters.end(); ++it) {
    for (set<OMPFilter *>::iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) {
    	//cout << "aggiunto filtro" << endl;
    	OMPFilter * filter = *it2;
      filtersInfos[filterId].interface = it->first;
      filtersInfos[filterId].numConstraints = filter->numConstraints;
      //********************************** PROBABILMENTE è QUI CHE DEVO AGIRE *******************************************//
      filtersInfos[filterId].window = filter->window;
      filtersInfos[filterId].occurrence = filter->occurrence;
      //**************************************** FINE MIO INTERVENTO ****************************************************//
      BFilter bf = 0;
      for (int i=0; i<filter->numConstraints; i++) {
    	  add(bf,filter->constraints[i].name);
      }
      for (int i=0; i<filter->numConstraints; i++) {
		string_t name = filter->constraints[i].name;
		map<string_t, int>::iterator currentIt = currentSize.find(name);
		nameConstrNames[name][currentIt->second]=bf;
		if(nameType[name]==INT) {
		  buildIntConstraint(((IntOMPConstraint3 *)nameConstraints[name])+currentIt->second, filter->constraints[i], filterId, bf);
		} else {
		  buildStringConstraint(((StringOMPConstraint3 *)nameConstraints[name])+currentIt->second, filter->constraints[i], filterId, bf);
		}
		currentIt->second++;
      }
      filterId++;
    }
  }
  consolidated = true;
  int totConstr=0;
  for(map<string_t,int>::iterator it=numConstraints.begin(); it!=numConstraints.end(); ++it) {
    totConstr+=it->second;
  }
  //cout << "TOTAL NUMBER OF CONSTRAINTS: " << totConstr << endl;
}

void ParallelFwd3::getStats(double *in, double *exec, double *res) {
  *in = inputTime;
  *exec = execTime;
  *res = resultsTime;
}

#if STATS==1
void ParallelFwd3::processMessage(OMPOutbox *outbox) {
  Timer t;
  t.start();
  BFilter bf = makeInput(outbox->message);
  inputTime += t.stop();
  t.start();
  computeResults(bf);
  execTime += t.stop();
  t.start();
  getMatchingInterfaces(outbox->outgoingInterfaces);
  resultsTime += t.stop();
}
#elif STATS==0
void ParallelFwd3::processMessage(OMPOutbox *outbox) {
  BFilter bf = makeInput(outbox->message);
  //********************************** PROBABILMENTE è QUI CHE DEVO AGIRE ********************************************//
  std::vector<float> windowsList;
  float lastPercentage = 0;
  float ws = 0;
  int ns = 0;
  int lastNs = 0;
  //**************************************** FINE MIO INTERVENTO *****************************************************//
  computeResults(bf, windowsList, ns, lastNs);
  //********************************** PROBABILMENTE è QUI CHE DEVO AGIRE ********************************************//
  //cout << windowsList.size() << endl;
  if (!windowsList.empty()){
	  //cout << "not empty" << endl;
	  sort(windowsList.begin(), windowsList.end());
	  ws = windowsList[(int)windowsList.size()/2];
  } else {
  	  //cout << "empty" << endl;
  }
  if (ns == 0){
	  lastPercentage = 0;
  } else {
	  lastPercentage = lastNs/ns;
  }
  outbox->ns = ns;
  outbox->ws = ws;
  outbox->last = lastPercentage;
	//  cout << "test: ns=" << ns << " ws= " << ws << endl;
  //cout << "ns: " << outbox->ns << " ws: " << outbox->ws << endl;
  //**************************************** FINE MIO INTERVENTO *****************************************************//
  getMatchingInterfaces(outbox->outgoingInterfaces);
  //cout << "beccate: " << outbox->outgoingInterfaces.size() << endl;
}
#endif

BFilter ParallelFwd3::makeInput(OMPMessage *message) {
  BFilter bf=0;  
  int dest = 0;
  for (int i=0; i<message->numAttributes; i++) {
    add(bf, message->attributes[i].name);
    string_t name = message->attributes[i].name;
    map<string_t, int>::iterator it = numConstraints.find(name);
    if (it==numConstraints.end()) continue;
    input.constraintsSize[dest] = it->second;
    input.constraints[dest] = nameConstraints.find(name)->second;
    input.constrNames[dest] = nameConstrNames.find(name)->second;
    input.values[dest] = message->attributes[i].value;
    dest++;
  }
  input.constraintsNum = dest;
  return bf;
}

void ParallelFwd3::computeResults(BFilter bf, std::vector<float>& windowsList, int& ns, int& lastNs) {
  for (int j=0; j<input.constraintsNum; j++) {
#pragma omp parallel for
    for (int i=0; i<input.constraintsSize[j]; i++) {
      evalConstraint(i, j, bf, windowsList, ns, lastNs);
    }
  }
}

void ParallelFwd3::getMatchingInterfaces(set<int> &results) {
  for (int i=0; i<numInterfaces; i++) {
    if (interfaces[i]==true) {
      results.insert(i); //----------------------->>> TOLTA!!
    	//results.insert(filtersInfos[i].interface); //----------------------->>> AGGIUNTA!!
    	interfaces[i] = false;
    }
  }
#pragma omp parallel for
  for(int i=0; i<numFilters; i++) {
    filtersCount[i] = 0x00u;
  }
}

void ParallelFwd3::buildIntConstraint(IntOMPConstraint3 *result, OMPConstraint &constraint, int filterId, BFilter bf) {
  result->op = constraint.op;
  result->value = constraint.value.intVal;
  result->filter = filterId;
}

void ParallelFwd3::buildStringConstraint(StringOMPConstraint3 *result, OMPConstraint &constraint, int filterId, BFilter bf) {
  result->op = constraint.op;
  memcpy(result->value, constraint.value.stringVal,STRING_VAL_LEN);
  result->filter = filterId;
}
